/**
 * README
 * 
 * Modificacion de la biblioteca jWave disponible en http://code.google.com/p/jwave/
 * Convertidos los tipos de datos a float para agilizar el tratamiendo de grandes arrays de datos
 * como son los que se tratan en analisis genomicos.
 * Los autores originales de las funciones y del código de la biblioteca cuando fue seleccionada en
 * Abril de 2012 eran:
 *        Christian Scheiblich
 *        Thomas Leduc
 *        sashi
 *        Pol Kennel
 *        Thomas Haider
 *        
 * Por supuesto, todo el merito, honores y demas menciones sobre esta adaptacion les corresponden a ellos
 * y nada mas que a ellos.
 * 
 * 
 * This is a modified version of jWave, available in http://code.google.com/p/jwave/
 * It has been replace the double datatype with float for performance reasons in very big arrays used in
 * bioinformatics like genomic analysis.
 * Original authors of functions and the library when it has been selected in 2012's April were:
 *        Christian Scheiblich
 *        Thomas Leduc
 *        sashi
 *        Pol Kennel
 *        Thomas Haider
 *        
 * Of course, all the merits and mentions about this adaptation have corresponded to them.
 * 
 */

package ev.maths.handlers;

import ev.maths.Complex;

/**
 * Transformada de Fourier Discreta
 * 
 * @date 01.08.2012
 * @author Enrique Vázquez de Luis
 */
public class DiscreteFourierTransform extends BasicTransform {

  /**
   * Constructor
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   */
  public DiscreteFourierTransform( ) {
  } // DiscreteFourierTransform

  /**
   * Transformada de Fourier unidimensional
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @see math.transform.jwave.handlers.BasicTransform#forward(double[])
   */
  @Override
  public float[ ] forward( float[ ] arrTime ) {

    int m = arrTime.length;
    float[ ] arrFreq = new float[ m ]; // result

    int n = m >> 1; // half of m

    for( int i = 0; i < n; i++ ) {

      int iR = i * 2;
      int iC = i * 2 + 1;

      arrFreq[ iR ] = 0;
      arrFreq[ iC ] = 0;

      float arg = (float) (-2. * Math.PI * i / n);

      for( int k = 0; k < n; k++ ) {

        int kR = k * 2;
        int kC = k * 2 + 1;

        float cos = (float) Math.cos( k * arg );
        float sin = (float) Math.sin( k * arg );

        arrFreq[ iR ] += arrTime[ kR ] * cos - arrTime[ kC ] * sin;
        arrFreq[ iC ] += arrTime[ kR ] * sin + arrTime[ kC ] * cos;

      } // k

      arrFreq[ iR ] /= n;
      arrFreq[ iC ] /= n;

    } // i

    return arrFreq;
  } // forward

  /**
   * Transformada inversa de Fourier discreta unidimensional
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   * @see math.transform.jwave.handlers.BasicTransform#reverse(double[])
   */
  @Override
  public float[ ] reverse( float[ ] arrFreq ) {

    int m = arrFreq.length;
    float[ ] arrTime = new float[ m ]; // result

    int n = m >> 1; // half of m

    for( int i = 0; i < n; i++ ) {

      int iR = i * 2;
      int iC = i * 2 + 1;

      arrTime[ iR ] = 0;
      arrTime[ iC ] = 0;

      float arg = (float) (2. * Math.PI * i / n);

      for( int k = 0; k < n; k++ ) {

        int kR = k * 2;
        int kC = k * 2 + 1;

        float cos = (float) Math.cos( k * arg );
        float sin = (float) Math.sin( k * arg );

        arrTime[ iR ] += arrFreq[ kR ] * cos - arrFreq[ kC ] * sin;
        arrTime[ iC ] += arrFreq[ kR ] * sin + arrFreq[ kC ] * cos;

      } // k

    } // i

    return arrTime;
  } // reverse

  /**
   * Transformada compleja de Fourier discreta y unidimensional
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   */
  public Complex[ ] forward( Complex[ ] arrTime ) {

    int n = arrTime.length;

    Complex[ ] arrFreq = new Complex[ n ]; // result

    for( int i = 0; i < n; i++ ) {

      arrFreq[ i ] = new Complex( ); // 0. , 0.

      double arg = -2. * Math.PI * (double)i / (double)n;

      for( int k = 0; k < n; k++ ) {

        double cos = Math.cos( k * arg );
        double sin = Math.sin( k * arg );

        double real = arrTime[ k ].getReal( );
        double imag = arrTime[ k ].getImag( );

        arrFreq[ i ].addReal( real * cos - imag * sin );
        arrFreq[ i ].addImag( real * sin + imag * cos );

      } // k

      arrFreq[ i ].mulReal(  1. / (double)n );
      arrFreq[ i ].mulImag(  1. / (double)n );

    } // i

    return arrFreq;
  } // forward

  /**
   * Transformada inversa de Fourier compleja, discreta y unidimensional.
   * 
   * @date 01.08.2012
   * @author Enrique Vázquez de Luis
   */
  public Complex[ ] reverse( Complex[ ] arrFreq ) {

    int n = arrFreq.length;
    Complex[ ] arrTime = new Complex[ n ]; // result

    for( int i = 0; i < n; i++ ) {

      arrTime[ i ] = new Complex( ); // 0. , 0. 

      double arg = 2. * Math.PI * (double)i / (double)n;

      for( int k = 0; k < n; k++ ) {

        double cos = Math.cos( k * arg );
        double sin = Math.sin( k * arg );

        double real = arrFreq[ k ].getReal( );
        double imag = arrFreq[ k ].getImag( );

        arrTime[ i ].addReal( real * cos - imag * sin );
        arrTime[ i ].addImag( real * sin + imag * cos );

      } // k

    } // i

    return arrTime;
  } // reverse
  


} // class
